﻿// File:        genericStructuredGraphicControl.js
// Module:      Chart
// Author:      Pavel A. Nikitin
/* Description: -------------------------------------------------->
 There are 4 methods of drawing, implemented in this class:
  - Canvas  ( 1 ) - suitable for Opera 9+, Firefox 1.5+, Safari
  - Generic ( 2 ) - suitable for all (maybe) browsers
  - VML     ( 3 ) - suitable for almost all new Internet Explorer's
 The method is determined by outputMethod field in constructor,
 and hence the caller itself should decide, wich of them to use.
 ----
 StructuredGraphicControl Class:
 - User-defined variables (on creation):
   - outputMethod - method of drawing
   - id - canvas name
   - x, y, width, height - absolute metrics of canvas
 - Other member variables:
   - resultString - automaticaly generated content string
 - Methods:
   - setColor (R, G, B) - sets a color for the further drawing
   - lineTo (x, y) - draws a line and moves the pen to x, y point
   - moveTo (x, y) - moves the pen to x, y point
   - getHtmlString () - generates the html code for the resulting graph
<--------------------------------------------------------------- */

// StructuredGraphicControl CLASS (constructor function)
function StructuredGraphicControl (x, y, width, height) {
	
	// An auxiliary CLASS for a simultaneously counting maximum and
	// miminum  between two numbers (for GENERIC_OUTPUT_METHOD).
	function StartFinish (start, finish) {
		if (start < finish) {
			this.start  = start;
			this.finish = finish;
		} else {
			this.start  = finish;
			this.finish = start;
		}
	}
	
	//Function that selects method of drawing corresponding to capabilities of user browser.
	this.SelectOutputMethod = function () {		
		var outputMethod = StructuredGraphicControl.GENERIC_OUTPUT_METHOD;
		if (navigator.userAgent.indexOf("Firefox") != -1)
		{
			var versionIndex = navigator.userAgent.indexOf("Firefox") + 8;
			var version = navigator.userAgent.substring(versionIndex);
			version = parseFloat(version);
			if(version >= 1.5) outputMethod = StructuredGraphicControl.CANVAS_OUTPUT_METHOD;
		} else
		if (navigator.userAgent.indexOf("Opera") != -1)
		{
			var versionIndex = navigator.userAgent.indexOf("Opera") + 6;
			var version = navigator.userAgent.substring(versionIndex, navigator.userAgent.indexOf(" ", versionIndex));
			version = parseFloat(version);
			if(version >= 9) outputMethod = StructuredGraphicControl.CANVAS_OUTPUT_METHOD;
		} else
		if (navigator.userAgent.indexOf("MSIE") != -1)
		{
			var versionIndex = navigator.userAgent.indexOf("MSIE") + 5;
			var version = navigator.userAgent.substring(versionIndex, navigator.userAgent.indexOf(";", versionIndex));
			version = parseFloat(version);
			if(version >= 5 && version <= 8) outputMethod = StructuredGraphicControl.VML_OUTPUT_METHOD;
		}
		return outputMethod;
	};
	// Creating a variable, that stores the output method evaluated
	// by SelectOutputMethod member function
	this.outputMethod = this.SelectOutputMethod();
	
	// Automaticaly generated content string
	var resultString = ""; //@private
	// Canvas name
	this.id = "StructuredGraphicControl-" + StructuredGraphicControl.count++;
	// Absolute metrics of canvas
	this.x = x;
	this.y = y;
	this.width  = width;
	this.height = height;
	
	// setColor sets a color for the further drawing (outputMethod dependent)
	this.setColor = function (r, g, b) {
		this.colorString = 'rgb(' + r + ',' + g + ',' + b + ')';
		switch (this.outputMethod) {
			case StructuredGraphicControl.CANVAS_OUTPUT_METHOD:
				resultString += 'context.stroke();' + 'context.strokeStyle="' + this.colorString + '";context.beginPath();';
			break;
		}
	}; // end of StructuredGraphicControl setColor method.
	// Here we set default color for drawing to Black:
	this.setColor(0, 0, 0);

	// lineTo draws a line and moves the pen to x_, y_ point (outputMethod dependent)
	this.lineTo = function (x_, y_) {
		x_ = Math.round(x_);
		y_ = Math.round(y_);
		switch (this.outputMethod) {
			case StructuredGraphicControl.CANVAS_OUTPUT_METHOD:
				// Example: context.lineTo(x,y);
				resultString += 'context.lineTo(' + x_ + ',' + y_ + ');';
			break;
			case StructuredGraphicControl.GENERIC_OUTPUT_METHOD:
				var x = x_ + this.x; // here we correct pen's X pos
				var y = y_ + this.y; // here we correct pen's Y pos
				if (x == this.penX) // drawing a vertical line
				{
					var p = new StartFinish (this.penY, y);
					for (var j = p.start; j <= p.finish; j++)
					{
						var style = "'top:" + j + "px;left:" + this.penX + "px; background-color:" + this.colorString + ";'";
						resultString += "<div class='px' style=" + style + "></div>";
					}
				} else  
				if (y == this.penY) // drawing a horizontal line // (x != this.penX)
				{
					var p = new StartFinish (this.penX, x);
					for(var i = p.start; i <= p.finish; i++)
					{
						var style = "'top:" + this.penY + "px;left:" + i + "px; background-color:" + this.colorString + ";'";
						resultString += "<div class='px' style=" + style + "></div>";
					}
				} else { // (x != this.penX) and (y != this.penY)
					// drawing a generic positioned line
					var tan = (y - this.penY) / (x - this.penX);
					var p = new StartFinish (this.penX, x);
					var currJ = Math.round(tan * (p.start - this.penX) + this.penY);
					for (var i = p.start; i < p.finish; i++)
					{
						nextJ = Math.round(tan * (i+1 - this.penX) + this.penY);
						var q = new StartFinish (currJ, nextJ);
						var half = ((q.finish - q.start)/2) + q.start;
						// top = j, left = i
						for (var j = q.start; j <= q.finish; j++)
						{
							var deltaX = (half > j) ^ (currJ < nextJ);
							var style = "'top:" + j + "px;left:" + (i + deltaX) + "px; background-color:" + this.colorString + ";'";
							resultString += "<div class='px' style=" + style + "></div>";
						}							
						currJ = nextJ;
					}
				}
				this.penX = x;
				this.penY = y;
			break;
			case StructuredGraphicControl.VML_OUTPUT_METHOD:
				var x = x_ + this.x; // here we correct pen's X pos
				var y = y_ + this.y; // here we correct pen's Y pos			
				resultString += 
					"<v:line strokecolor='" + this.colorString +
									 "'from='" + this.penX + "px," + this.penY + "px" +
									 "'to='" + x + "px," + y + "px'/>";
				this.penX = x;
				this.penY = y;
			break;
		}
	}; // end of StructuredGraphicControl lineTo method.
	
	// moveTo moves the pen to x, y point (outputMethod dependent)
	this.moveTo = function (x, y) {
		x = Math.round(x);
		y = Math.round(y);
		switch (this.outputMethod) {
			case StructuredGraphicControl.CANVAS_OUTPUT_METHOD:
				// Example: context.moveTo(x,y);
				// context.stroke and Context.beginPath are used
				// to  complete  the drawing  of line sequences.
				resultString += 'context.stroke(); context.beginPath(); context.moveTo(' + x + ',' + y + ');';
			break;
			case StructuredGraphicControl.GENERIC_OUTPUT_METHOD:
				// Actually, we are writing no data.
				this.penX = x + this.x;
				this.penY = y + this.y;
			break;	
			case StructuredGraphicControl.VML_OUTPUT_METHOD:
				// Actually, we are writing no data.
				this.penX = x + this.x;
				this.penY = y + this.y;
			break;	
		}
	}; // end of StructuredGraphicControl moveTo method.
	this.moveTo(0,0);
	
	// getHtmlString generates the html code for the resulting graph (outputMethod dependent)
	this.getHtmlString = function () {
		switch (this.outputMethod) {
			case StructuredGraphicControl.CANVAS_OUTPUT_METHOD:
				resultString = 
				"<canvas ID='" + this.id +
					"' width='" + this.width +
					"' height='" + this.height +
				"'></canvas>" +
				'<script type="text/javascript">' +
					// There is a opening script tag, so the data is
					// a  sequence of JavaScript commands  using the
					// canvas variable declared by this script.
					"var canvas = document.getElementById('" + this.id + "');" +
					"var context = canvas.getContext('2d');" +
					"context.beginPath();" +
					resultString +
					"context.stroke();" +
				'</script>' +
				'<style type="text/css">#' + this.id + 
					'{position:absolute;top:' + this.y + ';left:' + this.x + ';}' +
				'</style>';
			break;
			case StructuredGraphicControl.GENERIC_OUTPUT_METHOD:
				resultString = '<style>.px {font-size:0px;position:absolute;width:1px;height:1px;}</style>' + resultString;
			break;
			case StructuredGraphicControl.VML_OUTPUT_METHOD:
				resultString = '<style>v\\:* { behavior: url(#default#VML) }</style>' + resultString;
			break;
			default:
				resultString = "<B>Drawing method " + this.outputMethod + " not supported!</B>";
		}
		return resultString;
	}; // end of getHtmlString method.
} // end of StructuredGraphicControl class.

//Static block
StructuredGraphicControl.count = 0;

StructuredGraphicControl.CANVAS_OUTPUT_METHOD  = 1;
StructuredGraphicControl.GENERIC_OUTPUT_METHOD = 2;
StructuredGraphicControl.VML_OUTPUT_METHOD     = 3;